from flask import Flask, render_template, request, redirect, url_for, send_from_directory, flash,jsonify
import re
import os
from docx import Document
import requests
import json
import pymysql
import traceback
import face_recognition
import cv2
import numpy
# 导入库
import requests
import os.path
import  re
from PIL import Image
from io import BytesIO
from docx import Document
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.shared import Inches
from docx.shared import Pt
from docx.oxml.ns import qn
# 设置默认配置
MaxSearchPage = 20 # 收索页数
CurrentPage = 0 # 当前正在搜索的页数
DefaultPath = "pictures" # 默认储存位置
NeedSave = 0 # 是否需要储存
MaxImages = 2
DownLoadImages = 0
MinWidth = 10
MinHeight = 10
# 图片链接正则和下一页的链接正则
def imageFiler(content): # 通过正则获取当前页面的图片地址数组
    return re.findall('"objURL":"(.*?)"',content,re.S)
def nextSource(content): # 通过正则获取下一页的网址
    next = re.findall('<div id="page">.*<a href="(.*?)" class="n">',content,re.S)[0]
    if next:
        print("---------" + "http://image.baidu.com" + next)
        return next
    return None
# 清除特殊字符
def clean_filename(filename):
    return re.sub(r'@.*$', '', filename)

def spidler(source):
    content = requests.get(source).text  # 通过链接获取内容
    imageArr = imageFiler(content) # 获取图片数组
    global CurrentPage,NeedSave,DefaultPath,DownLoadImages,MaxImages

    print("当前页： " + str(CurrentPage) )
    for imageUrl in imageArr:
        if (DownLoadImages >= MaxImages):
            print(f"已经下载够{DownLoadImages}张图片，停止爬取。")
            return

        print('-+'*20)
        print("图片网页地址："+imageUrl)
        print('--' * 20)

        if NeedSave and DownLoadImages <MaxImages:  			# 如果需要保存图片则下载图片，否则不下载图片
            try:
                # 下载图片并设置超时时间,如果图片地址错误就不继续等待了
                picture = requests.get(imageUrl,timeout=10)
                picture.raise_for_status()
                #检查类型
                content_type = picture.headers.get('Content-Type')
                if not content_type or 'image' not in content_type:
                    print(f"非图片文件，跳过.")
                    continue
                #检查图片完整性
                img= Image.open(BytesIO(picture.content))
                # 验证图片文件
                img.verify()
                # 检验大小
                img = Image.open(BytesIO(picture.content))
                width, height = img.size
                if height >= width or width < MinWidth or height < MinHeight:
                    print(f"图片不符合宽度大于高度或分辨率要求，跳过。 URL: {imageUrl}")
                    continue
            except:
                print("下载错误! errorUrl:" + imageUrl)
                continue
            # 创建图片保存的路径
            # imageUrl = imageUrl.replace('/','').replace(':','').replace('?','')
            imageUrl1 = clean_filename(os.path.basename(imageUrl))
            basepath = os.getcwd()

            print("图片名" +imageUrl1 )
            pictureSavePath = basepath + "\\" + DefaultPath +"\\"+ imageUrl1
            pictureSavePath = pictureSavePath.split("?")[0]
            print("图片保存地址："+pictureSavePath)
            # fp = open(pictureSavePath,'wb') # 以写入二进制的方式打开文件
            # fp.write(picture.content)
            # fp.close()
            try:
                with open(pictureSavePath, 'wb') as fp:
                    fp.write(picture.content)
                DownLoadImages += 1

            except:
                print("保存失败：pictureSavePath open failed,we passed that one")
                DownLoadImages -= 1
                continue

    if CurrentPage <= MaxSearchPage and DownLoadImages <MaxImages:    # 继续下一页爬取
        if nextSource(content):
            CurrentPage += 1
            # 爬取完毕后通过下一页地址继续爬取
            spidler("http://image.baidu.com" + nextSource(content))

#爬虫的开启方法
def  beginSearch(document,theme,page=1,save=0,savePath="pictures/"):
    # (page:爬取页数,save:是否储存,savePath:默认储存路径)
    global MaxSearchPage,NeedSave,DefaultPath,DownLoadImages
    MaxSearchPage = page
    NeedSave = save					# 是否保存，值0不保存，1保存
    DefaultPath = savePath				# 图片保存的位置
    key = theme
    StartSource = "http://image.baidu.com/search/flip?tn=baiduimage&ie=utf-8&word=" + str(key) + "&ct=201326592&v=flip" # 分析链接可以得到,替换其`word`值后面的数据来搜索关键词
    spidler(StartSource)
    if DownLoadImages > 0:
        create_document(document)

# 创建包含图片的Word文档
def create_document(document):
    global DefaultPath
    # document = Document()
    # document.add_heading('爬取的图片', level=1)

    basepath = os.getcwd()
    image_path = os.path.join(basepath, DefaultPath)
    for image_file in os.listdir(image_path):
        if image_file.endswith(('jpg', 'jpeg', 'png')):
            img_path = os.path.join(image_path, image_file)
            document.add_paragraph().alignment = WD_PARAGRAPH_ALIGNMENT.CENTER
            document.add_picture(img_path, width=Inches(4.0))  # 设置图片宽度为4英寸
            last_paragraph = document.paragraphs[-1]
            last_paragraph.alignment = WD_PARAGRAPH_ALIGNMENT.CENTER

    # document_save_path = os.path.join(basepath, '爬取的图片.docx')
    # document.save(document_save_path)
    # print(f"文档已保存至: {document_save_path}")


API_KEY = "enIdUFiBAJPJl2zlUIawpkcw"
SECRET_KEY = "ScjsfobqmfOyvsGC9DYiTgd3SzgCJv2L"

app = Flask(__name__)
app.config['SECRET_KEY'] = 'your_secret_key'
app.config['UPLOAD_FOLDER'] = 'generated_files'
app.secret_key = 'ScjsfobqmfOyvsGC9DYiTgd3SzgCJv2L'
if not os.path.exists('generated_files'):
    os.makedirs('generated_files')
@app.route('/')
def login():
    return render_template('login.html')
@app.route('/resetcode')
def resetcode():
    return render_template('resetcode.html')

@app.route('/registuser', methods=['POST'])
def getRigistRequest():
    # 连接数据库
    db = pymysql.connect(host="localhost", user="root", password="Zqs629629.", database="mydatabases", charset="utf8")
    cursor = db.cursor()

    # 获取输入框内容
    user = request.form.get('user')  # 用户昵称
    phone = request.form.get('phone')  # 手机号
    password = request.form.get('password')
    password2 = request.form.get('password2')
    # 判断两次输入密码是否一致
    if not validate_phone_number(phone):
        flash('手机号格式不正确，请重新输入！', 'error')
        return redirect(url_for('login'))
    global picture
    if password == password2:
        # 验证手机号格式（这里需要根据实际的格式要求来编写验证逻辑）

        # 准备SQL插入语句
        sql = "INSERT INTO User(user, phone, password,picture) VALUES (%s, %s, %s,%s)"
        try:
            face()
            cursor.execute(sql, (user, phone, password,picture))
            db.commit()
            flash('注册成功，请登录！', 'success')
            return redirect(url_for('login'))
        except Exception as e:
            traceback.print_exc()
            db.rollback()
            flash('注册失败，请重试！', 'error')
            return redirect(url_for('login'))
        finally:
            db.close()
    else:
        flash('两次输入的密码不一致，请重新输入！', 'error')
        return redirect(url_for('login'))

def reset_pass(phone, password):
    conn = pymysql.connect(host='localhost', user='root', password='Zqs629629.', database='mydatabases', charset='utf8')
    cursor = conn.cursor()
    sql = "UPDATE user SET password=%s WHERE phone=%s"
    try:
        resetflag = cursor.execute(sql, (password, phone))
        conn.commit()
        cursor.close()
        conn.close()
        if (resetflag == 1):
            print("修改成功")
            return 1
        else:
            print("修改失败！")
            return 0
    except Exception as e:
        print(f"系统错误: {e}")
        return False

@app.route('/resetpass', methods=['POST'])
def resetpass():
    userphone = request.form.get('userphone')
    resetpass = request.form.get('resetpass')
    print("路由获得手机号：" + userphone)
    print("路由获得新密码：" + resetpass)
    flag = reset_pass(userphone, resetpass)
    if flag:
        return jsonify({"data": 1})
    else:
        return jsonify({"data": 0})

def face():
    global picture
    cap = cv2.VideoCapture(0)
    cap.set(3, 640)
    cap.set(4, 480)
    cap.set(10, 100)
    while True:
        success, img = cap.read()
        txt = "Once you see the stable box, press q to end the recognition"
        font = cv2.FONT_HERSHEY_DUPLEX
        cv2.putText(img, txt, (10, 50), font, 0.5, (0, 0, 0), 1)
        face_locations = face_recognition.face_locations(img)
        for (top, right, bottom, left) in face_locations:
            img = cv2.rectangle(img, (left, top), (right, bottom), (0, 0, 255), 2)
        cv2.imshow('frame', img)
        #按下q触发人脸识别
        if cv2.waitKey(1) & 0xFF == ord('q'):
            unknown_image = img
            # known_image = face_recognition.load_image_file("images/me.jpg")
            # ldh_encoding = face_recognition.face_encodings(known_image)[0]
            if len(face_recognition.face_encodings(unknown_image)) > 0:
                unknown_encoding = face_recognition.face_encodings(unknown_image)[0]
                picture=encoding_FaceStr(unknown_encoding)
                break
            else:
                print("No faces found in the image!")

    cap.release()
    cv2.destroyAllWindows()

def encoding_FaceStr(image_face_encoding):
    # 将numpy array类型转化为列表
    encoding__array_list = image_face_encoding.tolist()
    # 将列表里的元素转化为字符串
    encoding_str_list = [str(i) for i in encoding__array_list]
    # 拼接列表里的字符串
    encoding_str = ','.join(encoding_str_list)
    return encoding_str

def decoding_FaceStr(encoding_str):
    # print("encoding=%s" % (encoding_str))
    # 将字符串转为numpy ndarray类型，即矩阵
    # 转换成一个list

    dlist = encoding_str[0][0].strip(' ').split(',')
    # dlist=" ".join(encoding_str)
    # 将list中str转换为float
    dfloat = list(map(float, dlist))
    face_encoding = numpy.array(dfloat)
    return face_encoding

def validate_phone_number(phone_number):
    # 定义一个简单的手机号正则表达式，例如中国的手机号通常是以1开头的11位数字
    pattern = r'^1[3-9]\d{9}$'

    # 使用re.match来检查手机号是否符合正则表达式
    if re.match(pattern, phone_number):
        return True
    else:
        return False
# 获取登录参数及处理

@app.route('/login', methods=['GET', 'POST'])
def getLoginRequest():
    if request.method == 'POST':
        # 使用request.form.get来获取POST数据
        username = request.form.get('user')
        password = request.form.get('password')

        # 构建参数化查询以防止SQL注入
        sql = "SELECT * FROM user WHERE user=%s AND password=%s"
        params = (username, password)
        sql2="SELECT picture FROM user where user=%s AND password=%s"
        params2=(username, password)
        # 连接数据库
        db = pymysql.connect(host="localhost", user="root", password="Zqs629629.", database="mydatabases",
                             charset="utf8")
        try:
            with db.cursor() as cursor:
                cursor.execute(sql, params)
                results = cursor.fetchall()
                cursor.execute(sql2, params2)
                if len(results) == 1:
                    results2 = cursor.fetchall()
                    results2 = decoding_FaceStr(results2)
                    face2(results2)
                    return render_template('index.html') # 重定向到成功页面
                else:
                    flash('用户名或密码不正确', 'error')
                    return render_template('login.html') # 重定向回登录页面
        except Exception as e:
            flash('登录失败：' + str(e), 'error')  # 显示具体错误信息
            traceback.print_exc()
        finally:
            db.close()
    else:
        # 如果是GET请求，显示登录表单
        return render_template('login.html')

def face2(data):

    cap = cv2.VideoCapture(0)
    cap.set(3, 640)
    cap.set(4, 480)
    cap.set(10, 100)
    while True:
        success, img = cap.read()
        txt = "Once you see the stable box, press q to end the recognition"
        font = cv2.FONT_HERSHEY_DUPLEX
        cv2.putText(img, txt, (10, 50), font, 0.5, (0, 0, 0), 1)
        face_locations = face_recognition.face_locations(img)
        for (top, right, bottom, left) in face_locations:
            img = cv2.rectangle(img, (left, top), (right, bottom), (0, 0, 255), 2)
        cv2.imshow('frame', img)

        if cv2.waitKey(1) & 0xFF == ord('q'):

            unknown_image = img
            #known_image = face_recognition.load_image_file("images/me.jpg")
            ldh_encoding = data
            # print(ldh_encoding)
            if len(face_recognition.face_encodings(unknown_image)) > 0:
                unknown_encoding = face_recognition.face_encodings(unknown_image)[0]
                results = face_recognition.compare_faces([ldh_encoding], unknown_encoding, tolerance=0.39)[0]
                if results:
                   break
            else:
                print("No faces found in the image!")

    cap.release()
    cv2.destroyAllWindows()

@app.route('/', methods=['GET', 'POST'])
def index():
    if request.method == 'POST':
        theme = request.form.get('theme')
        main_points = request.form.get('main_points')
        # 模拟API调用
        response_content = get_generated_content(theme, main_points)
        return render_template('index.html', theme=theme, main_points=main_points, response_content=response_content)

    return render_template('index.html')

@app.route('/generate_document', methods=['POST'])
def generate_document():
    theme = request.form.get('theme')
    main_points = request.form.get('main_points')
    response_content = request.form.get('response_content')
    
    # 生成Word文档
    filename = generate_word_document(theme, main_points, response_content)
    return send_from_directory(app.config['UPLOAD_FOLDER'], filename, as_attachment=True)

def get_generated_content(theme, main_points):
    # 这是一个模拟的API调用，实际使用时应替换为实际的API调用
    # 示例API调用
    # 这里应该填入你自己的API Key和Secret Key


    url = "https://aip.baidubce.com/rpc/2.0/ai_custom/v1/wenxinworkshop/chat/eb-instant?access_token=" + get_access_token()
    s = theme+main_points
    # 将用户输入转换为JSON格式的payload
    payload = json.dumps({
        "messages": [
            {
                "role": "user",
                "content": s
            }
        ]
    })
    # payload = json.dumps({"theme": theme, "main_points": main_points})
    # headers = {'Content-Type': 'application/json'}
    headers = {
        'Content-Type': 'application/json'
    }

    # response = requests.post(url, headers=headers, data=payload)
    res = requests.post(url, headers=headers, data=payload).json()
    return res['result']


    # 模拟生成的内容
    # return f"生成的内容基于主题：{theme}\n主要要点：{main_points}"

def parse_and_format_text(paragraph, text):
    """解析并格式化文本，处理被*包裹的加粗内容"""
    lines = text.split('\n')
    for line in lines:
        if line.startswith('* '):
            line = line[2:]  # 去掉开头的 '* '
            parts = line.split('**')
            for i, part in enumerate(parts):
                if i % 2 == 1:
                    run = paragraph.add_run(part)
                    run.bold = True
                else:
                    run = paragraph.add_run(part)
                run.font.name = '宋体'
                run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
                run.font.size = Pt(14)
            paragraph.add_run('\n')  # 添加换行
        else:
            run = paragraph.add_run(line)
            run.font.name = '宋体'
            run._element.rPr.rFonts.set(qn('w:eastAsia'), '宋体')
            run.font.size = Pt(14)
            paragraph.add_run('\n')  # 添加换行
def generate_word_document(theme, main_points, content):
    document = Document()
    document.add_heading(theme, 0)


    # document.add_paragraph(main_points)

    # 调用开启的方法就可以通过关键词搜索图片了
    beginSearch(document,theme,page=5, save=1)  # page=5是下载前5页，save=1保存图片

    # document.add_paragraph(content)
    # 添加主要要点并设置缩进和格式
    main_points_paragraph = document.add_paragraph()
    main_points_paragraph.paragraph_format.left_indent = Pt(28.35)  # 设置缩进2格（约0.5英寸）
    parse_and_format_text(main_points_paragraph, main_points)

    # 添加生成的内容并设置缩进和格式
    content_paragraph = document.add_paragraph()
    content_paragraph.paragraph_format.left_indent = Pt(28.35)  # 设置缩进2格（约0.5英寸）
    parse_and_format_text(content_paragraph, content)

    filename = f"{theme}.docx"
    filepath = os.path.join(app.config['UPLOAD_FOLDER'], filename)
    document.save(filepath)
    return filename


# 获取访问令牌的函数
def get_access_token():
    """
    使用API Key和Secret Key生成鉴权签名（Access Token）。
    :return: 返回access_token，如果发生错误则返回None。
    """
    # 构造请求URL，指向百度智能云的OAuth 2.0令牌服务
    url = "https://aip.baidubce.com/oauth/2.0/token"

    # 设置请求参数，包括授权类型、客户端ID和客户端密钥
    params = {
        "grant_type": "client_credentials",
        "client_id": API_KEY,
        "client_secret": SECRET_KEY
    }

    # 发送POST请求，并从响应中获取access_token
    response = requests.post(url, params=params).json()

    # 返回获取到的access_token
    return response.get("access_token")

if __name__ == '__main__':
    app.run(debug=True)
